#include <zenom/GraphModel.hpp>
#include <zenox/minilog.hpp>
#include <zenox/pragma.hpp>

namespace zenom {

void NodeModel::on_update(NodeRole role, std::any value) {
    switch (role) {
        case NodeRole::NodeType: {
            type = std::any_cast<std::string>(value);
        } break;
        case NodeRole::NodeTitle: {
            title = std::any_cast<std::string>(value);
        } break;
        case NodeRole::NodePortCount: {
            auto new_size = std::any_cast<size_t>(value);
            if (ports.size()) [[unlikely]] {
                for (auto &port: ports) {
                    if (port.in_node)
                        port.unlink();
                }
                ports.clear();
            }
            ports.resize(new_size);
        } break;
    }
}

void PortModel::on_update(PortRole role, std::any value) {
    switch (role) {
        case PortRole::PortName: {
            name = std::any_cast<std::string>(value);
        } break;
        case PortRole::PortDataType: {
            data_type = std::any_cast<std::type_index>(value);
        } break;
        case PortRole::PortInlineValue: {
            inline_value = std::any_cast<std::string>(value);
        } break;
        case PortRole::PortVisible: {
            visible = std::any_cast<bool>(value);
        } break;
    }
}

void PortModel::on_link(NodeModel *node) {
    ZENO_ASSERT(node);
    ZENO_ASSERT(node->ports, _.empty() || this < _.data() || this >= _.data() + _.size(), "loop detected");
    ZENO_ASSERT(!in_node);
    ZENO_ASSERT(node->out_ports.insert(this).second);
    in_node = node;
}

void PortModel::on_unlink() {
    ZENO_ASSERT(in_node);
    ZENO_ASSERT(in_node->out_ports.erase(this));
    in_node = nullptr;
}

void GraphModel::on_update(GraphRole role, std::any value) {
    switch (role) {
        case GraphRole::GraphName: {
            name = std::any_cast<std::string>(value);
        } break;
    }
}

NodeModel *GraphModel::on_new_node() {
    auto model = ZENO_ASSERT(nodes.insert(std::make_unique<NodeModel>()), _.second).first->get();
    return ZENO_ASSERT(model);
}

void GraphModel::on_delete_node(NodeModel *node) {
    ZENO_ASSERT(nodes.erase(node));
}

void GraphModel::delete_node(NodeModel *node) {
    for (auto &port: node->ports) {
        if (port.in_node)
            port.unlink();
    }
    for (auto *port: node->out_ports) {
        if (ZENO_ASSERT(port)->in_node)
            port->unlink();
    }
    view->on_delete_node(node);
    on_delete_node(node);
}

}
